package com.jlf.starter.advice.reactive.excepton;

import com.jlf.core.error.Code;
import com.jlf.core.error.Errors;
import com.jlf.core.output.ResultException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ResponseStatusException;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 统一异常处理
 *
 * @author wujr
 * 2023/8/2
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/8/2 1.0 新增]
 */
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        String uri = request.uri().getPath();
        if (!uri.startsWith("/rest")){
            return super.getErrorAttributes(request, options);
        }
        Map<String, Object> errorAttributes = new HashMap<>(8);// super.getErrorAttributes(request, options);
        Throwable error = super.getError(request);
        MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations
                .from(error.getClass(), MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);

        Code errorCode = determineHttpStatus(error, responseStatusAnnotation);

        //必须设置, 否则会报错, 因为 DefaultErrorWebExceptionHandler 的 renderErrorResponse 方法会获取此属性, 重新实现 DefaultErrorWebExceptionHandler也可.
        errorAttributes.put("status", HttpStatus.OK.value());
        errorAttributes.put("code", errorCode.getError());
        String message = error.getMessage();
        errorAttributes.put("message", StringUtils.isBlank(errorCode.getMessage()) ? message : errorCode.getMessage());

        // html view用
        errorAttributes.put("timestamp", new Date());
        // html view 用
        errorAttributes.put("requestId", request.exchange().getRequest().getId());

        log.warn("统一异常：timestamp: {}，message: {}，method：{}，path：{}, exception：", errorAttributes.get("timestamp"),
                error.getMessage(), request.method(), request.path(), error);

        request.exchange().getAttributes().put("__log_object", errorAttributes);
        request.exchange().getResponse().getHeaders().add("code", String.valueOf(errorCode.getError()));
        return errorAttributes;
    }
    /**
     * 从DefaultErrorWebExceptionHandler中复制过来的
     * @param error 异常
     * @param responseStatusAnnotation 响应结果码的封装
     * @return 响应码
     */
    private Code determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        if (error instanceof ResponseStatusException) {
            int errorCode = ((ResponseStatusException)error).getStatusCode().value();
            return Errors.from(errorCode);
        }else if (error instanceof ResultException e){
            return Errors.from(e.getError());
        }
        Optional<HttpStatus> httpStatus = responseStatusAnnotation.getValue("code", HttpStatus.class);
        return httpStatus.map(status -> Errors.from(status.value())).orElse(Errors.SYS_INTERNAL_ERROR);
    }
}
